// --- 特性测试宏，必须放在所有 include 之前 ---
// 启用 POSIX.1-2008 标准 (包含 sigaction, SA_RESTART 等)
#define _POSIX_C_SOURCE 200809L

// --- 头文件 ---
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>    // fork, sleep, pause, getpid, kill
#include <signal.h>    // signal, sigaction, SIGUSR2, SIGCHLD, sigemptyset
#include <sys/wait.h>  // wait, waitpid
#include <sys/types.h> // pid_t

// --- 全局变量 ---
// 子进程用来统计信号次数 (volatile sig_atomic_t 保证在信号处理函数中安全访问)
volatile sig_atomic_t sigusr2_count = 0;
const int TARGET_SIGNAL_COUNT = 5;

// --- 子进程的 SIGUSR2 信号处理函数 ---
void child_sigusr2_handler(int signum)
{
    if (signum == SIGUSR2)
    {
        sigusr2_count++;
        // 为了清晰，可以在这里打印接收信息（但注意 printf 在信号处理函数中不是完全安全的）
        // printf("Child (%d): Received SIGUSR2. Count = %d\n", getpid(), sigusr2_count);
        // fflush(stdout); // 如果打印，需要刷新缓冲区
    }
}

// --- 父进程的 SIGCHLD 信号处理函数 (可选方案) ---
// volatile sig_atomic_t child_exited_flag = 0; // 配合 SIGCHLD 使用的标志
// void parent_sigchld_handler(int signum) {
//     if (signum == SIGCHLD) {
//         // 清理僵尸进程，防止累积
//         int status;
//         while (waitpid(-1, &status, WNOHANG) > 0); // 循环清理所有已退出的子进程
//         child_exited_flag = 1;
//         // printf("Parent (%d): Caught SIGCHLD, child likely exited.\n", getpid());
//         // fflush(stdout);
//     }
// }

int main()
{
    pid_t pid = fork();

    if (pid < 0)
    {
        // fork 失败
        perror("fork failed");
        exit(EXIT_FAILURE);
    }
    else if (pid == 0)
    {
        // --- 子进程代码 ---
        printf("Child process (%d) started.\n", getpid());
        fflush(stdout);

        // 设置 SIGUSR2 的信号处理函数
        struct sigaction sa_usr2;
        sa_usr2.sa_handler = child_sigusr2_handler;
        sigemptyset(&sa_usr2.sa_mask); // 处理期间不阻塞其他信号
        sa_usr2.sa_flags = SA_RESTART; // 如果在系统调用中被中断，尝试重启系统调用

        if (sigaction(SIGUSR2, &sa_usr2, NULL) == -1)
        {
            perror("Child: sigaction(SIGUSR2) failed");
            exit(EXIT_FAILURE);
        }

        // 循环等待信号，直到达到目标次数
        while (sigusr2_count < TARGET_SIGNAL_COUNT)
        {
            // pause() 会使进程挂起，直到捕获到一个信号
            // 这是一个比空循环更高效的等待方式
            pause();
        }

        // 达到目标次数后退出
        printf("Received %d signals, child exit!\n", TARGET_SIGNAL_COUNT);
        fflush(stdout);     // 确保信息打印后再退出
        exit(EXIT_SUCCESS); // 正常退出
    }
    else
    {
        // --- 父进程代码 ---
        printf("Parent process (%d) started, child PID is %d.\n", getpid(), pid);
        fflush(stdout);

        // (可选方案: 设置 SIGCHLD 信号处理函数)
        // struct sigaction sa_chld;
        // sa_chld.sa_handler = parent_sigchld_handler;
        // sigemptyset(&sa_chld.sa_mask);
        // sa_chld.sa_flags = SA_RESTART | SA_NOCLDSTOP; // 只关心终止，不关心停止
        // if (sigaction(SIGCHLD, &sa_chld, NULL) == -1) {
        //     perror("Parent: sigaction(SIGCHLD) failed");
        //     // 不致命，可以继续使用 wait/waitpid
        // }

        // 等待一小会儿，确保子进程已经设置好了信号处理函数
        sleep(1);

        // 循环发送信号，直到子进程退出
        int signals_sent = 0;
        while (1)
        {
            // 检查子进程是否已经退出 (非阻塞方式)
            int status;
            pid_t result = waitpid(pid, &status, WNOHANG); // WNOHANG: 非阻塞检查

            if (result == pid)
            {
                // 子进程已经退出
                // printf("Parent (%d): waitpid detected child exit.\n", getpid());
                // fflush(stdout);
                break; // 跳出发送循环
            }
            else if (result == 0)
            {
                // 子进程仍在运行，发送信号
                printf("Parent (%d): Sending SIGUSR2 to child (%d) (Signal #%d)\n", getpid(), pid, signals_sent + 1);
                fflush(stdout);
                if (kill(pid, SIGUSR2) == -1)
                {
                    // 如果 kill 失败，很可能是子进程刚好在 waitpid 和 kill 之间退出了
                    perror("Parent: kill failed");
                    // 再次检查子进程状态，或者直接退出循环
                    pid_t final_check = waitpid(pid, &status, WNOHANG);
                    if (final_check != 0)
                    { // 子进程确实退出了或发生错误
                        break;
                    }
                }
                signals_sent++;
                // 只有在子进程还在运行时才 sleep
                sleep(2);
            }
            else
            { // result == -1
                // waitpid 出错
                perror("Parent: waitpid error");
                break;
            }
            // (可选方案: 使用 SIGCHLD 标志)
            // if (child_exited_flag) {
            //     printf("Parent (%d): Detected child exit via SIGCHLD flag.\n", getpid());
            //     fflush(stdout);
            //     break;
            // }
        }

        // 阻塞式 wait/waitpid (推荐):
        // 即使上面使用了 waitpid(WNOHANG) 或 SIGCHLD，
        // 在这里再调用一次阻塞的 wait/waitpid 可以确保完全清理子进程资源并获取最终状态。
        // 如果子进程已经被清理，它会立即返回。
        int final_status;
        waitpid(pid, &final_status, 0); // 阻塞等待特定子进程

        printf("Child terminated.\n");
        fflush(stdout);
        exit(EXIT_SUCCESS); // 父进程正常退出
    }

    // 理论上不应执行到这里
    return 0;
}